package top.huxy.boot.util.obj;

import com.alibaba.fastjson.JSON;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.method.HandlerMethod;
import top.huxy.boot.annotation.Operation;
import top.huxy.boot.annotation.RequestRemake;
import top.huxy.boot.model.entity.ExceptionLog;
import top.huxy.boot.model.entity.VisitLog;
import top.huxy.boot.service.impl.ExceptionLogServiceImpl;
import top.huxy.boot.service.impl.VisitLogServiceImpl;
import top.huxy.boot.util.IPAddressUtils;
import top.huxy.boot.util.RequestUtils;
import top.huxy.boot.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
 * @Classname RequestDetail
 * @Description 如果做了日常日志，表示这次请求状态是false
 * @Date 2021/9/24 9:15
 * @Created by huxy
 */
@NoArgsConstructor
@Slf4j
@Getter
public class ThreadLocalRequestDetail {
    private static final ThreadLocal<ThreadLocalRequestDetail> local = new ThreadLocal<>();

    protected HttpServletRequest request;

    protected HandlerMethod handler;

    protected boolean isOk;

    @Getter(AccessLevel.NONE)
    protected long startTime;

    @Getter(AccessLevel.NONE)
    protected long endTime;

    @Getter(AccessLevel.NONE)
    protected boolean isEnd;

    protected long createTime = System.currentTimeMillis();

    protected VisitLog visitLog;

    protected ExceptionLog exceptionLog;

    /**
     * 在拦截器中实例化该请求详情
     */
    public ThreadLocalRequestDetail(HttpServletRequest request, HandlerMethod handler) {
        if (handler != null) {
            this.handler = handler;
        }
        this.startTime = System.currentTimeMillis();
        this.request = request;
    }

    /**
     * 在拦截器中调用
     */
    public static void setLocalRequestDetail(ThreadLocalRequestDetail requestDetail) {
        destroy();
        local.set(requestDetail);
    }

    /**
     * 可在任意方法中获取，如果不是请求中获取，则会是null
     *
     * @return
     */
    public static ThreadLocalRequestDetail current() {
        return local.get();
    }

    /**
     * 请确保拦截器调用此方法
     */
    public static void destroy() {
        ThreadLocalRequestDetail threadLocalRequestDetail = local.get();
        if (threadLocalRequestDetail != null) {
            threadLocalRequestDetail.doLog();
        }
        local.remove();
    }

    private void doLog() {
        if (isEnd) {
            return;
        }
        isEnd = true;
        if (visitLog != null) {
            VisitLogServiceImpl.getInstance().saveVisitLog2Redis(visitLog);
            visitLog = null;
        }
        if (exceptionLog != null) {
            ExceptionLogServiceImpl.getInstance().saveExceptionLog2Redis(exceptionLog);
            exceptionLog = null;
        }
    }


    public void logVisitor(boolean status) {
        if (visitLog != null) {
            log.error("请确保visitLog没有被赋值");
        }
        //获取执行的方法信息

        //http请求方法，get,post等
        String requestMethod = null;

        //获取请求的所有参数信息
        String requestParams = null;

        //请求ip
        String ip = null;

        //ip对应的城市来源信息
        String cityInfo = null;

        //重装系统和浏览器等信息
        Map<String, String> osAndBrowser = null;

        //uri信息
        String requestURI = null;

        //生成请求ID
        String requestID = null;

        String remake = null;

        if (handler != null) {
            RequestRemake requestRemake = handler.getMethodAnnotation(RequestRemake.class);
            if (requestRemake != null) {
                remake = StringUtils.or(requestRemake.value(), null);
            } else {
                Operation operation = handler.getMethodAnnotation(Operation.class);
                if (operation != null) {
                    remake = StringUtils.or(operation.value(), null);
                }
            }
        }


        if (request != null) {
            requestID = RequestUtils.getRequestID(request);
            requestMethod = request.getMethod();
            requestParams = JSON.toJSONString(request.getParameterMap());
            if (requestParams.length() > 9500) {
                requestParams = requestParams.substring(0, 9500);
            }
            ip = IPAddressUtils.getIpAddress(request);
            cityInfo = IPAddressUtils.getCityInfo(ip);
            osAndBrowser = RequestUtils.parseOsAndBrowser(request);
            requestURI = request.getRequestURI();
            if (requestURI.length() > 250) {
                requestURI = requestURI.substring(0, 250);
            }

        }

        //执行时间
        long time = System.currentTimeMillis() - startTime;

        //生成日志对象
        visitLog = new VisitLog(requestID,
                StringUtils.or(handler, null),
                requestMethod,
                requestURI,
                requestParams,
                ip,
                cityInfo,
                osAndBrowser != null ? osAndBrowser.get("os") : null,
                osAndBrowser != null ? osAndBrowser.get("browser") : null,
                time,
                status,
                createTime,
                remake);
    }

    public void logError(Throwable th) {
//        if (exceptionLog != null) {
//            log.error("请确保exceptionLog没有被赋值");
//        }
        if (isEnd) {
//            log.error("重复logError");
            return;
        }
        //堆栈信息
        String stackTrace = null;

        //请求ID
        String requestID = null;

        //异常消息
        String message = null;

        //异常名
        String exceptionName = null;

        //必须有请求对象，而且请求对象初始化了请求id，才获得请求id
        if (request != null&&RequestUtils.hasRequestId(request)) {
            requestID = RequestUtils.getRequestID(request);
        }

        if (th != null) {
            stackTrace = StringUtils.getStackTrace(th);
            message = th.getMessage();
            exceptionName = th.getClass().getName();
        }

        //生成异常日志对象
        exceptionLog = new ExceptionLog(requestID, stackTrace, message, exceptionName);
        logVisitor(false);
        doLog();
    }


    public void setOk(boolean b) {
        this.isOk = b;
    }
}
